#!/usr/bin/python

from base import *

import os
import wx
import fm
import sys
import time
import cPickle
import paranoia

def read_user_settings_file():
	p=fm.get_user_settings_module_path()
	if not os.path.exists(p):
		make_initial_user_settings_file()
		
	fd=open(p)
	content=fd.read() #?? need check all is read?
	fd.close()
	
	return content
	
def write_user_settings_file(file_content):
	fm.safer_save(fm.get_user_settings_module_path(),file_content)
	
def make_initial_user_settings_file():
	p=fm.get_user_settings_module_path()
	
	assert not os.path.exists(p)
	
	eol=fm.platform_eol()
	
	content=eol.join((
		'# Farmhand user settings python module',
		'',
		'# See "default_settings.dump" file in this directory for available settings',
		'# You can open this file from inside farmhand by going to menu Settings->Edit Settings File',
		'',
		'import fm',
		'settings=fm.settings',
		'',
		'# example:',
		"#settings['textbar.width']=50"))
		
	fd=open(p,'w')
	fd.write(content)
	fd.close()

def append_automatically_managed_section(file_content,section_content,eol): # will overwrite if section already there
	BEGINNING=eol+eol+'## <FARMHAND_AUTOMATICALLY_MANAGED_SECTION>'+eol
	ENDING=eol+'## </FARMHAND_AUTOMATICALLY_MANAGED_SECTION>'+eol
		
	start=file_content.rfind(BEGINNING)
	end=file_content.rfind(ENDING)
	
	# ?? make it more robust? find and pull out section with any eol scheme?
	if start<>-1 and end<>-1 and start<end:
		# allow whitespace after the section
		if end+len(ENDING)==len(file_content) or file_content[end+len(ENDING):].isspace():
			file_content=file_content[:start]
			
	file_content += BEGINNING + section_content + ENDING
	
	return file_content
	

Wisconsin=()
	
class _SettingsChangeEventManager:
	def __init__(me):
		me.listeners=[]
		me.flag_queue_events=False
		
	def add(me,f,observed_settings):
		me.listeners.append(fm.WeakMethod(f))

	def queue_events(me):
		me.flag_queue_events=True
		
	def broadcast_queued_events(me):
		me.flag_queue_events=False
		#??
		
	def broadcast_changed_settings(me,*names):
		event_payload=struct(src=fm.settings,changed_settings=names,__event__='Settings_Change')
		
		dead_listeners=[]
		
		for f in me.listeners:
			try:
				f(event_payload)				
			except ReferenceError:
				dead_listeners.append(f)
			except wx._core.PyDeadObjectError: dead_listeners.append(f) # ?? damn you windows!
				
		for corpse in dead_listeners:
			me.listeners.remove(corpse)
			if me in Wisconsin:
				me.have_some_fun(corpse)
	
class _GlobalSettingsSingleton(dict):
	
	_MAX_MODE=3
	DEFAULTS_MODE, USER_MANUALLY_SET, USER_SET_THROUGH_GUI = range(_MAX_MODE)
	
	def __init__(me):
		dict.__init__(me)
		
		me.user_set_through_gui_settings_modified = False
		me.user_set_through_gui_settings=[]
		
		me.default_settings=[]
		
		me.modes=[me.DEFAULTS_MODE]
		
		me._change_event_manager=_SettingsChangeEventManager()
		
	def push_mode(me,mode):
		assert 0<=mode<me._MAX_MODE
		me.modes.append(mode)
		
	def pop_mode(me):
		return me.modes.pop()
		
	def load_user_settings(me,need_reload=True):
		me.push_mode(me.USER_MANUALLY_SET)
		
		if fm.configuration_dir not in sys.path:
			sys.path.insert(0,fm.configuration_dir)
		
		try:
			mod=__import__('settings', globals(), locals(), [], 0)

			assert mod.__file__.startswith(os.path.join(fm.configuration_dir,"settings.py")),\
				"wrong setting.py '%s'"%mod.__file__ #?? need safer way to import this
			
			if need_reload:
				reload(mod)
		finally:
			me.pop_mode()
			
	def for_reference_dump_default_settings(me):
		
		p=fm.get_default_settings_dump_path()
		
		if os.path.exists(p) and not fm.debug.ON:
			return
			
		fd=open(p,'w')
		fd.write('## AUTOMATICALLY GENERATED FARMHAND DEFAULT SETTINGS DUMP (%s)\n'%time.asctime()
			+'## This dump is here for reference, editing this file will have no effect.\n'
			+'## To change the settings, use the gui or edit\n'
			+"##    '%s'\n"%fm.get_user_settings_module_path()
			+'## which can be opened through the menu Settings->Edit Settings File\n\n')
			
		for k in me.default_settings:
			fd.write("%-40s = %r\n"%("settings['%s']"%k,me[k]))
			
		fd.close()
		
	def on_change_call(me,f,*observed_settings):
		me._change_event_manager.add(f,observed_settings)
	
	def __setitem__(me,k,v):
		#if me.mode == me.AUTO_MANAGED_MODE:
		#	if k not in me.auto_managed_settings:
		#		me.auto_managed_settings.append(k)
		mode=me.modes[-1]
		if mode == me.DEFAULTS_MODE:
			me.default_settings.append(k)
		elif mode == me.USER_SET_THROUGH_GUI:
			if k not in me.user_set_through_gui_settings:
				me.user_set_through_gui_settings.append(k)
			me.user_set_through_gui_settings_modified=True
			
		dict.__setitem__(me,k,v)
		
		if me.modes[-1] <> me.DEFAULTS_MODE:
			me._change_event_manager.broadcast_changed_settings( k )
		
	
	def __getitem__(me,k):
		try: return dict.__getitem__(me,k)
		
		except KeyError: pass
		
		try:
			head,tail=k.rsplit('.',1)
		except ValueError:#this must be at the root, no '.'
			raise KeyError(k)
			
		base=dict.get(me,head+'.__inherit__',None)
		if base is None: raise KeyError(k)
		
		try: return dict.__getitem__(me,base+'.'+tail)
		except KeyError: raise KeyError(k)
	
	def on_application_close(me):
		if me.user_set_through_gui_settings_modified: # ?? this appears to trigger unnecessarily
			file_content=read_user_settings_file() 
			
			eol=fm.figure_out_eol(file_content)
			
			body=['', 'if 1: # indent for clarity', '\t# This sections stores settings configured through the GUI',
				'','\timport fm # just in case','\tsettings=fm.settings','',
				'\tsettings.push_mode(settings.USER_SET_THROUGH_GUI)','',]
			
			for k in me.user_set_through_gui_settings:
				body.append("\t%-40s = %r"%("settings['%s']"%k,me[k]))

			body.extend(('','\tsettings.pop_mode()',''))
			
			file_content=append_automatically_managed_section(file_content,eol.join(body),eol)
			
			write_user_settings_file(file_content)
			
def _PersistentStateSingleton():
	if not os.path.exists(fm.get_persistent_state_savefile_path()):
		return _PersistentStateSingleton_class()
	else:
		return cPickle.load(open(fm.get_persistent_state_savefile_path()))
		
class _PersistentStateSingleton_class(dict):
	def __init__(me):
		dict.__init__(me)
		
	def __getitem__(me,k):
		try:
			return dict.__getitem__(me,k)
		except KeyError:
			return None
			
	def on_application_close(me):
		cPickle.dump(me,open(fm.get_persistent_state_savefile_path(),'w'))
